Added required_features for issue #1570.
authorJames Bendig <jbendig@starbytesoftware.com>
Wed, 8 Feb 2017 00:20:21 +0000 (18:20 -0600)
committerJames Bendig <jbendig@starbytesoftware.com>
Wed, 8 Feb 2017 00:20:21 +0000 (18:20 -0600)
Based on PR #2056 by @tsurai and PR #2325 by @JanLikar

src/cargo/core/manifest.rs
src/cargo/ops/cargo_compile.rs
src/cargo/util/toml.rs
tests/required-features.rs [new file with mode: 0644]

index 2354eadfe34236207ece2bb808cab85e884e3c65..6c71cd9b94b49f6ecd8a230a783e7b72517c2e37 100644 (file)
@@ -185,6 +185,7 @@ pub struct Target {
     kind: TargetKind,
     name: String,
     src_path: PathBuf,
+    required_features: Option<Vec<String>>,
     tested: bool,
     benched: bool,
     doc: bool,
@@ -305,6 +306,7 @@ impl Target {
             kind: TargetKind::Bin,
             name: String::new(),
             src_path: src_path,
+            required_features: None,
             doc: false,
             doctest: false,
             harness: true,
@@ -326,10 +328,12 @@ impl Target {
         }
     }
 
-    pub fn bin_target(name: &str, src_path: PathBuf) -> Target {
+    pub fn bin_target(name: &str, src_path: PathBuf,
+                      required_features: Option<Vec<String>>) -> Target {
         Target {
             kind: TargetKind::Bin,
             name: name.to_string(),
+            required_features: required_features,
             doc: true,
             ..Target::with_path(src_path)
         }
@@ -349,7 +353,8 @@ impl Target {
 
     pub fn example_target(name: &str,
                           crate_targets: Vec<LibKind>,
-                          src_path: PathBuf) -> Target {
+                          src_path: PathBuf,
+                          required_features: Option<Vec<String>>) -> Target {
         let kind = if crate_targets.is_empty() {
             TargetKind::ExampleBin
         } else {
@@ -359,24 +364,29 @@ impl Target {
         Target {
             kind: kind,
             name: name.to_string(),
+            required_features: required_features,
             benched: false,
             ..Target::with_path(src_path)
         }
     }
 
-    pub fn test_target(name: &str, src_path: PathBuf) -> Target {
+    pub fn test_target(name: &str, src_path: PathBuf,
+                       required_features: Option<Vec<String>>) -> Target {
         Target {
             kind: TargetKind::Test,
             name: name.to_string(),
+            required_features: required_features,
             benched: false,
             ..Target::with_path(src_path)
         }
     }
 
-    pub fn bench_target(name: &str, src_path: PathBuf) -> Target {
+    pub fn bench_target(name: &str, src_path: PathBuf,
+                        required_features: Option<Vec<String>>) -> Target {
         Target {
             kind: TargetKind::Bench,
             name: name.to_string(),
+            required_features: required_features,
             tested: false,
             ..Target::with_path(src_path)
         }
@@ -385,6 +395,7 @@ impl Target {
     pub fn name(&self) -> &str { &self.name }
     pub fn crate_name(&self) -> String { self.name.replace("-", "_") }
     pub fn src_path(&self) -> &Path { &self.src_path }
+    pub fn required_features(&self) -> Option<&Vec<String>> { self.required_features.as_ref() }
     pub fn kind(&self) -> &TargetKind { &self.kind }
     pub fn tested(&self) -> bool { self.tested }
     pub fn harness(&self) -> bool { self.harness }
index ec13b2a2e8af91118b3eee7a822b5822b1f9ec64..27b8610293faf2a180205e51e9954bf0ef0b0d44 100644 (file)
@@ -22,7 +22,8 @@
 //!       previously compiled dependency
 //!
 
-use std::collections::HashMap;
+use std::collections::{HashMap, HashSet};
+use std::default::Default;
 use std::path::PathBuf;
 use std::sync::Arc;
 
@@ -187,7 +188,8 @@ pub fn compile_ws<'a>(ws: &Workspace<'a>,
         }
     } else {
         let root_package = ws.current()?;
-        generate_targets(root_package, profiles, mode, filter, release)?;
+        let all_features = resolve_with_overrides.features(root_package.package_id());
+        generate_targets(root_package, profiles, mode, filter, all_features, release)?;
         pkgids.push(root_package.package_id());
     };
 
@@ -204,8 +206,9 @@ pub fn compile_ws<'a>(ws: &Workspace<'a>,
             panic!("`rustc` and `rustdoc` should not accept multiple `-p` flags")
         }
         (Some(args), _) => {
+            let all_features = resolve_with_overrides.features(to_builds[0].package_id());
             let targets = generate_targets(to_builds[0], profiles,
-                                           mode, filter, release)?;
+                                           mode, filter, all_features, release)?;
             if targets.len() == 1 {
                 let (target, profile) = targets[0];
                 let mut profile = profile.clone();
@@ -218,8 +221,9 @@ pub fn compile_ws<'a>(ws: &Workspace<'a>,
             }
         }
         (None, Some(args)) => {
+            let all_features = resolve_with_overrides.features(to_builds[0].package_id());
             let targets = generate_targets(to_builds[0], profiles,
-                                           mode, filter, release)?;
+                                           mode, filter, all_features, release)?;
             if targets.len() == 1 {
                 let (target, profile) = targets[0];
                 let mut profile = profile.clone();
@@ -233,8 +237,9 @@ pub fn compile_ws<'a>(ws: &Workspace<'a>,
         }
         (None, None) => {
             for &to_build in to_builds.iter() {
+                let all_features = resolve_with_overrides.features(to_build.package_id());
                 let targets = generate_targets(to_build, profiles, mode,
-                                               filter, release)?;
+                                               filter, all_features, release)?;
                 package_targets.push((to_build, targets));
             }
         }
@@ -313,6 +318,7 @@ fn generate_targets<'a>(pkg: &'a Package,
                         profiles: &'a Profiles,
                         mode: CompileMode,
                         filter: &CompileFilter,
+                        features: Option<&HashSet<String>>,
                         release: bool)
                         -> CargoResult<Vec<(&'a Target, &'a Profile)>> {
     let build = if release {&profiles.release} else {&profiles.dev};
@@ -325,13 +331,13 @@ fn generate_targets<'a>(pkg: &'a Package,
         CompileMode::Doc { .. } => &profiles.doc,
         CompileMode::Doctest => &profiles.doctest,
     };
-    match *filter {
+    let mut targets = match *filter {
         CompileFilter::Everything => {
             match mode {
                 CompileMode::Bench => {
-                    Ok(pkg.targets().iter().filter(|t| t.benched()).map(|t| {
+                    pkg.targets().iter().filter(|t| t.benched()).map(|t| {
                         (t, profile)
-                    }).collect::<Vec<_>>())
+                    }).collect::<Vec<_>>()
                 }
                 CompileMode::Test => {
                     let deps = if release {
@@ -352,16 +358,16 @@ fn generate_targets<'a>(pkg: &'a Package,
                             base.push((t, deps));
                         }
                     }
-                    Ok(base)
+                    base
                 }
                 CompileMode::Build | CompileMode::Check => {
-                    Ok(pkg.targets().iter().filter(|t| {
+                    pkg.targets().iter().filter(|t| {
                         t.is_bin() || t.is_lib()
-                    }).map(|t| (t, profile)).collect())
+                    }).map(|t| (t, profile)).collect()
                 }
                 CompileMode::Doc { .. } => {
-                    Ok(pkg.targets().iter().filter(|t| t.documented())
-                          .map(|t| (t, profile)).collect())
+                    pkg.targets().iter().filter(|t| t.documented())
+                       .map(|t| (t, profile)).collect()
                 }
                 CompileMode::Doctest => {
                     if let Some(t) = pkg.targets().iter().find(|t| t.is_lib()) {
@@ -370,7 +376,7 @@ fn generate_targets<'a>(pkg: &'a Package,
                         }
                     }
 
-                    Ok(Vec::new())
+                    Vec::new()
                 }
             }
         }
@@ -409,6 +415,7 @@ fn generate_targets<'a>(pkg: &'a Package,
                             }
                         };
                         debug!("found {} `{}`", desc, name);
+
                         targets.push((t, profile));
                     }
                     Ok(())
@@ -418,9 +425,37 @@ fn generate_targets<'a>(pkg: &'a Package,
                 find(tests, "test", Target::is_test, test)?;
                 find(benches, "bench", Target::is_bench, &profiles.bench)?;
             }
-            Ok(targets)
+            targets
+        }
+    };
+
+    //Collect the targets that are libraries or have all required features available.
+    let no_features = HashSet::new();
+    let features = features.unwrap_or(&no_features);
+    let mut compatible_targets = Vec::with_capacity(targets.len());
+    for (target, profile) in targets.drain(0..) {
+        if target.is_lib() || match target.required_features() {
+            Some(f) => !f.iter().any(|f| !features.contains(f)),
+            None => true,
+        } {
+            compatible_targets.push((target, profile));
+            continue;
+        }
+
+        if let CompileFilter::Only { .. } = *filter {
+            let required_features = target.required_features().unwrap();
+            let quoted_required_features: Vec<String> = required_features.iter()
+                                                                         .map(|s| format!("`{}`",s))
+                                                                         .collect();
+            bail!("target `{}` requires the features: {}\n\
+                  Consider enabling them by passing e.g. `--features=\"{}\"`",
+                  target.name(),
+                  quoted_required_features.join(", "),
+                  required_features.join(" "));
         }
     }
+
+    Ok(compatible_targets)
 }
 
 /// Parse all config files to learn about build configuration. Currently
index d12c73c679ebed73127ac2b264bd6f5b0286096c..c9ee9b2459dd8c31d377e42335d2ac65018ba150 100644 (file)
@@ -932,6 +932,7 @@ struct TomlTarget {
     plugin: Option<bool>,
     proc_macro: Option<bool>,
     harness: Option<bool>,
+    required_features: Option<Vec<String>>,
 }
 
 #[derive(RustcDecodable, Clone)]
@@ -961,6 +962,7 @@ impl TomlTarget {
             plugin: None,
             proc_macro: None,
             harness: None,
+            required_features: None
         }
     }
 
@@ -1125,7 +1127,8 @@ fn normalize(package_root: &Path,
                     false => PathValue::Path(Path::new("src").join("main.rs"))
                 }
             });
-            let mut target = Target::bin_target(&bin.name(), package_root.join(path.to_path()));
+            let mut target = Target::bin_target(&bin.name(), package_root.join(path.to_path()),
+                                                bin.required_features.clone());
             configure(bin, &mut target);
             dst.push(target);
         }
@@ -1154,7 +1157,8 @@ fn normalize(package_root: &Path,
             let mut target = Target::example_target(
                 &ex.name(),
                 crate_types,
-                package_root.join(path.to_path())
+                package_root.join(path.to_path()),
+                ex.required_features.clone()
             );
             configure(ex, &mut target);
             dst.push(target);
@@ -1169,7 +1173,8 @@ fn normalize(package_root: &Path,
                 PathValue::Path(default(test))
             });
 
-            let mut target = Target::test_target(&test.name(), package_root.join(path.to_path()));
+            let mut target = Target::test_target(&test.name(), package_root.join(path.to_path()),
+                                                 test.required_features.clone());
             configure(test, &mut target);
             dst.push(target);
         }
@@ -1183,7 +1188,8 @@ fn normalize(package_root: &Path,
                 PathValue::Path(default(bench))
             });
 
-            let mut target = Target::bench_target(&bench.name(), package_root.join(path.to_path()));
+            let mut target = Target::bench_target(&bench.name(), package_root.join(path.to_path()),
+                                                  bench.required_features.clone());
             configure(bench, &mut target);
             dst.push(target);
         }
diff --git a/tests/required-features.rs b/tests/required-features.rs
new file mode 100644 (file)
index 0000000..53b5041
--- /dev/null
@@ -0,0 +1,565 @@
+extern crate cargotest;
+extern crate hamcrest;
+
+use cargotest::support::{project, execs};
+use hamcrest::{assert_that, existing_file, not};
+
+#[test]
+fn build_bin_default_features() {
+    let p = project("foo")
+        .file("Cargo.toml", r#"
+            [project]
+            name = "foo"
+            version = "0.0.1"
+            authors = []
+
+            [features]
+            default = ["a"]
+            a = []
+
+            [[bin]]
+            name = "foo"
+            required-features = ["a"]
+        "#)
+        .file("src/main.rs", "fn main() {}");
+
+    assert_that(p.cargo_process("build"),
+                execs().with_status(0));
+    assert_that(&p.bin("foo"), existing_file());
+
+    assert_that(p.cargo_process("build").arg("--no-default-features"),
+                execs().with_status(0));
+    assert_that(&p.bin("foo"), not(existing_file()));
+
+    assert_that(p.cargo_process("build").arg("--bin=foo"),
+                execs().with_status(0));
+    assert_that(&p.bin("foo"), existing_file());
+
+    assert_that(p.cargo_process("build").arg("--bin=foo").arg("--no-default-features"),
+                execs().with_status(101).with_stderr("\
+error: target `foo` requires the features: `a`
+Consider enabling them by passing e.g. `--features=\"a\"`
+"));
+    assert_that(&p.bin("foo"), not(existing_file()));
+}
+
+#[test]
+fn build_bin_arg_features() {
+    let p = project("foo")
+        .file("Cargo.toml", r#"
+            [project]
+            name = "foo"
+            version = "0.0.1"
+            authors = []
+
+            [features]
+            a = []
+
+            [[bin]]
+            name = "foo"
+            required-features = ["a"]
+        "#)
+        .file("src/main.rs", "fn main() {}");
+
+    assert_that(p.cargo_process("build").arg("--features").arg("a"),
+                execs().with_status(0));
+    assert_that(&p.bin("foo"), existing_file());
+}
+
+#[test]
+fn build_bin_multiple_required_features() {
+    let p = project("foo")
+        .file("Cargo.toml", r#"
+            [project]
+            name = "foo"
+            version = "0.0.1"
+            authors = []
+
+            [features]
+            default = ["a", "b"]
+            a = []
+            b = ["a"]
+            c = []
+
+            [[bin]]
+            name = "foo_1"
+            path = "src/foo_1.rs"
+            required-features = ["b", "c"]
+
+            [[bin]]
+            name = "foo_2"
+            path = "src/foo_2.rs"
+            required-features = ["a"]
+        "#)
+        .file("src/foo_1.rs", "fn main() {}")
+        .file("src/foo_2.rs", "fn main() {}");
+
+    assert_that(p.cargo_process("build"),
+                execs().with_status(0));
+
+    assert_that(&p.bin("foo_1"), not(existing_file()));
+    assert_that(&p.bin("foo_2"), existing_file());
+
+    assert_that(p.cargo_process("build").arg("--features").arg("c"),
+                execs().with_status(0));
+
+    assert_that(&p.bin("foo_1"), existing_file());
+    assert_that(&p.bin("foo_2"), existing_file());
+
+    assert_that(p.cargo_process("build").arg("--no-default-features"),
+                execs().with_status(0));
+
+    assert_that(&p.bin("foo_1"), not(existing_file()));
+    assert_that(&p.bin("foo_2"), not(existing_file()));
+}
+
+#[test]
+fn build_example_default_features() {
+    let p = project("foo")
+        .file("Cargo.toml", r#"
+            [project]
+            name = "foo"
+            version = "0.0.1"
+            authors = []
+
+            [features]
+            default = ["a"]
+            a = []
+
+            [[example]]
+            name = "foo"
+            required-features = ["a"]
+        "#)
+        .file("examples/foo.rs", "fn main() {}");
+
+    assert_that(p.cargo_process("build").arg("--example=foo"),
+                execs().with_status(0));
+    assert_that(&p.bin("examples/foo"), existing_file());
+
+    assert_that(p.cargo_process("build").arg("--example=foo").arg("--no-default-features"),
+                execs().with_status(101).with_stderr("\
+error: target `foo` requires the features: `a`
+Consider enabling them by passing e.g. `--features=\"a\"`
+"));
+    assert_that(&p.bin("examples/foo"), not(existing_file()));
+}
+
+#[test]
+fn build_example_arg_features() {
+    let p = project("foo")
+        .file("Cargo.toml", r#"
+            [project]
+            name = "foo"
+            version = "0.0.1"
+            authors = []
+
+            [features]
+            a = []
+
+            [[example]]
+            name = "foo"
+            required-features = ["a"]
+        "#)
+        .file("examples/foo.rs", "fn main() {}");
+
+    assert_that(p.cargo_process("build").arg("--example=foo").arg("--features").arg("a"),
+                execs().with_status(0));
+    assert_that(&p.bin("examples/foo"), existing_file());
+}
+
+#[test]
+fn build_example_multiple_required_features() {
+    let p = project("foo")
+        .file("Cargo.toml", r#"
+            [project]
+            name = "foo"
+            version = "0.0.1"
+            authors = []
+
+            [features]
+            default = ["a", "b"]
+            a = []
+            b = ["a"]
+            c = []
+
+            [[example]]
+            name = "foo_1"
+            required-features = ["b", "c"]
+
+            [[example]]
+            name = "foo_2"
+            required-features = ["a"]
+        "#)
+        .file("examples/foo_1.rs", "fn main() {}")
+        .file("examples/foo_2.rs", "fn main() {}");
+
+    assert_that(p.cargo_process("build").arg("--example=foo_1"),
+                execs().with_status(101).with_stderr("\
+error: target `foo_1` requires the features: `b`, `c`
+Consider enabling them by passing e.g. `--features=\"b c\"`
+"));
+    assert_that(p.cargo("build").arg("--example=foo_2"),
+                execs().with_status(0));
+
+    assert_that(&p.bin("examples/foo_1"), not(existing_file()));
+    assert_that(&p.bin("examples/foo_2"), existing_file());
+
+    assert_that(p.cargo_process("build").arg("--example=foo_1")
+                .arg("--features").arg("c"),
+                execs().with_status(0));
+    assert_that(p.cargo("build").arg("--example=foo_2")
+                .arg("--features").arg("c"),
+                execs().with_status(0));
+
+    assert_that(&p.bin("examples/foo_1"), existing_file());
+    assert_that(&p.bin("examples/foo_2"), existing_file());
+
+    assert_that(p.cargo_process("build").arg("--example=foo_1")
+                .arg("--no-default-features"),
+                execs().with_status(101).with_stderr("\
+error: target `foo_1` requires the features: `b`, `c`
+Consider enabling them by passing e.g. `--features=\"b c\"`
+"));
+    assert_that(p.cargo("build").arg("--example=foo_2")
+                .arg("--no-default-features"),
+                execs().with_status(101).with_stderr("\
+error: target `foo_2` requires the features: `a`
+Consider enabling them by passing e.g. `--features=\"a\"`
+"));
+
+    assert_that(&p.bin("examples/foo_1"), not(existing_file()));
+    assert_that(&p.bin("examples/foo_2"), not(existing_file()));
+}
+
+#[test]
+fn test_default_features() {
+    let p = project("foo")
+        .file("Cargo.toml", r#"
+            [project]
+            name = "foo"
+            version = "0.0.1"
+            authors = []
+
+            [features]
+            default = ["a"]
+            a = []
+
+            [[test]]
+            name = "foo"
+            required-features = ["a"]
+        "#)
+        .file("tests/foo.rs", "#[test]\nfn test() {}");
+
+    assert_that(p.cargo_process("test"),
+                execs().with_status(0).with_stderr(format!("\
+[COMPILING] foo v0.0.1 ({})
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[RUNNING] target[/]debug[/]deps[/]foo-[..][EXE]", p.url()))
+                .with_stdout("
+running 1 test
+test test ... ok
+
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+
+"));
+
+    assert_that(p.cargo_process("test").arg("--no-default-features"),
+                execs().with_status(0).with_stderr(format!("\
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]"))
+                .with_stdout(""));
+
+    assert_that(p.cargo_process("test").arg("--test=foo"),
+                execs().with_status(0).with_stderr(format!("\
+[COMPILING] foo v0.0.1 ({})
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[RUNNING] target[/]debug[/]deps[/]foo-[..][EXE]", p.url()))
+                .with_stdout("
+running 1 test
+test test ... ok
+
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+
+"));
+
+    assert_that(p.cargo_process("test").arg("--test=foo").arg("--no-default-features"),
+                execs().with_status(101).with_stderr("\
+error: target `foo` requires the features: `a`
+Consider enabling them by passing e.g. `--features=\"a\"`
+"));
+}
+
+#[test]
+fn test_arg_features() {
+    let p = project("foo")
+        .file("Cargo.toml", r#"
+            [project]
+            name = "foo"
+            version = "0.0.1"
+            authors = []
+
+            [features]
+            a = []
+
+            [[test]]
+            name = "foo"
+            required-features = ["a"]
+        "#)
+        .file("tests/foo.rs", "#[test]\nfn test() {}");
+
+    assert_that(p.cargo_process("test").arg("--features").arg("a"),
+                execs().with_status(0).with_stderr(format!("\
+[COMPILING] foo v0.0.1 ({})
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[RUNNING] target[/]debug[/]deps[/]foo-[..][EXE]", p.url()))
+                .with_stdout("
+running 1 test
+test test ... ok
+
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+
+"));
+}
+
+#[test]
+fn test_multiple_required_features() {
+    let p = project("foo")
+        .file("Cargo.toml", r#"
+            [project]
+            name = "foo"
+            version = "0.0.1"
+            authors = []
+
+            [features]
+            default = ["a", "b"]
+            a = []
+            b = ["a"]
+            c = []
+
+            [[test]]
+            name = "foo_1"
+            required-features = ["b", "c"]
+
+            [[test]]
+            name = "foo_2"
+            required-features = ["a"]
+        "#)
+        .file("tests/foo_1.rs", "#[test]\nfn test() {}")
+        .file("tests/foo_2.rs", "#[test]\nfn test() {}");
+
+    assert_that(p.cargo_process("test"),
+                execs().with_status(0).with_stderr(format!("\
+[COMPILING] foo v0.0.1 ({})
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[RUNNING] target[/]debug[/]deps[/]foo_2-[..][EXE]", p.url()))
+                .with_stdout("
+running 1 test
+test test ... ok
+
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+
+"));
+
+    assert_that(p.cargo_process("test").arg("--features").arg("c"),
+                execs().with_status(0).with_stderr(format!("\
+[COMPILING] foo v0.0.1 ({})
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[RUNNING] target[/]debug[/]deps[/]foo_1-[..][EXE]
+[RUNNING] target[/]debug[/]deps[/]foo_2-[..][EXE]", p.url()))
+                .with_stdout("
+running 1 test
+test test ... ok
+
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+
+
+running 1 test
+test test ... ok
+
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+
+"));
+
+    assert_that(p.cargo_process("test").arg("--no-default-features"),
+                execs().with_status(0).with_stderr(format!("\
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]"))
+                .with_stdout(""));
+}
+
+#[test]
+fn bench_default_features() {
+    let p = project("foo")
+        .file("Cargo.toml", r#"
+            [project]
+            name = "foo"
+            version = "0.0.1"
+            authors = []
+
+            [features]
+            default = ["a"]
+            a = []
+
+            [[bench]]
+            name = "foo"
+            required-features = ["a"]
+        "#)
+        .file("benches/foo.rs", r#"
+            #![feature(test)]
+            extern crate test;
+
+            #[bench]
+            fn bench(_: &mut test::Bencher) {
+            }"#);
+
+    assert_that(p.cargo_process("bench"),
+                execs().with_status(0).with_stderr(format!("\
+[COMPILING] foo v0.0.1 ({})
+[FINISHED] release [optimized] target(s) in [..]
+[RUNNING] target[/]release[/]deps[/]foo-[..][EXE]", p.url()))
+                .with_stdout("
+running 1 test
+test bench ... bench: [..] 0 ns/iter (+/- 0)
+
+test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
+
+"));
+
+    assert_that(p.cargo_process("bench").arg("--no-default-features"),
+                execs().with_status(0).with_stderr(format!("\
+[FINISHED] release [optimized] target(s) in [..]"))
+                .with_stdout(""));
+
+    assert_that(p.cargo_process("bench").arg("--bench=foo"),
+                execs().with_status(0).with_stderr(format!("\
+[COMPILING] foo v0.0.1 ({})
+[FINISHED] release [optimized] target(s) in [..]
+[RUNNING] target[/]release[/]deps[/]foo-[..][EXE]", p.url()))
+                .with_stdout("
+running 1 test
+test bench ... bench: [..] 0 ns/iter (+/- 0)
+
+test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
+
+"));
+
+    assert_that(p.cargo_process("bench").arg("--bench=foo").arg("--no-default-features"),
+                execs().with_status(101).with_stderr("\
+error: target `foo` requires the features: `a`
+Consider enabling them by passing e.g. `--features=\"a\"`
+"));
+}
+
+#[test]
+fn bench_arg_features() {
+    let p = project("foo")
+        .file("Cargo.toml", r#"
+            [project]
+            name = "foo"
+            version = "0.0.1"
+            authors = []
+
+            [features]
+            a = []
+
+            [[bench]]
+            name = "foo"
+            required-features = ["a"]
+        "#)
+        .file("benches/foo.rs", r#"
+            #![feature(test)]
+            extern crate test;
+
+            #[bench]
+            fn bench(_: &mut test::Bencher) {
+            }"#);
+
+    assert_that(p.cargo_process("bench").arg("--features").arg("a"),
+                execs().with_status(0).with_stderr(format!("\
+[COMPILING] foo v0.0.1 ({})
+[FINISHED] release [optimized] target(s) in [..]
+[RUNNING] target[/]release[/]deps[/]foo-[..][EXE]", p.url()))
+                .with_stdout("
+running 1 test
+test bench ... bench: [..] 0 ns/iter (+/- 0)
+
+test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
+
+"));
+}
+
+#[test]
+fn bench_multiple_required_features() {
+    let p = project("foo")
+        .file("Cargo.toml", r#"
+            [project]
+            name = "foo"
+            version = "0.0.1"
+            authors = []
+
+            [features]
+            default = ["a", "b"]
+            a = []
+            b = ["a"]
+            c = []
+
+            [[bench]]
+            name = "foo_1"
+            required-features = ["b", "c"]
+
+            [[bench]]
+            name = "foo_2"
+            required-features = ["a"]
+        "#)
+        .file("benches/foo_1.rs", r#"
+            #![feature(test)]
+            extern crate test;
+
+            #[bench]
+            fn bench(_: &mut test::Bencher) {
+            }"#)
+        .file("benches/foo_2.rs", r#"
+            #![feature(test)]
+            extern crate test;
+
+            #[bench]
+            fn bench(_: &mut test::Bencher) {
+            }"#);
+
+    assert_that(p.cargo_process("bench"),
+                execs().with_status(0).with_stderr(format!("\
+[COMPILING] foo v0.0.1 ({})
+[FINISHED] release [optimized] target(s) in [..]
+[RUNNING] target[/]release[/]deps[/]foo_2-[..][EXE]", p.url()))
+                .with_stdout("
+running 1 test
+test bench ... bench: [..] 0 ns/iter (+/- 0)
+
+test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
+
+"));
+
+    assert_that(p.cargo_process("bench").arg("--features").arg("c"),
+                execs().with_status(0).with_stderr(format!("\
+[COMPILING] foo v0.0.1 ({})
+[FINISHED] release [optimized] target(s) in [..]
+[RUNNING] target[/]release[/]deps[/]foo_1-[..][EXE]
+[RUNNING] target[/]release[/]deps[/]foo_2-[..][EXE]", p.url()))
+                .with_stdout("
+running 1 test
+test bench ... bench: [..] 0 ns/iter (+/- 0)
+
+test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
+
+
+running 1 test
+test bench ... bench: [..] 0 ns/iter (+/- 0)
+
+test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
+
+"));
+
+    assert_that(p.cargo_process("bench").arg("--no-default-features"),
+                execs().with_status(0).with_stderr(format!("\
+[FINISHED] release [optimized] target(s) in [..]"))
+                .with_stdout(""));
+}
+